// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;

import 'package:args/command_runner.dart';
import 'package:path/path.dart' as path;

import 'environment.dart';
import 'felt_config.dart';
import 'package_lock.dart';

class GenerateBuilderJsonCommand extends Command<bool> {
  @override
  String get description =>
    'Generates JSON for the engine_v2 builders to build and copy all artifacts, '
    'compile all test bundles, and run all test suites on all platforms.';

  @override
  String get name => 'generate-builder-json';

  @override
  FutureOr<bool>? run() {
    final PackageLock packageLock = PackageLock();
    final FeltConfig config = FeltConfig.fromFile(
      path.join(environment.webUiTestDir.path, 'felt_config.yaml')
    );
    final String configString = generate(config, packageLock);
    final io.File configFile = io.File(path.join(
      environment.flutterDirectory.path,
      'ci',
      'builders',
      'linux_web_engine.json',
    ));
    configFile.writeAsStringSync('$configString\n');
    return true;
  }

  String generate(FeltConfig config, PackageLock packageLock) {
    final Map<String, dynamic> outputJson = <String, dynamic>{
      '_comment': 'THIS IS A GENERATED FILE. Do not edit this file directly.',
      '_comment2': 'See `generate_builder_json.dart` for the generator code',
      'builds': <dynamic>[
        _getArtifactBuildStep(),
        for (final TestBundle bundle in config.testBundles)
          _getBundleBuildStep(bundle),
      ],
      'tests': _getAllTestSteps(config.testSuites, packageLock)
    };
    return const JsonEncoder.withIndent('  ').convert(outputJson);
  }

  Map<String, dynamic> _getArtifactBuildStep() {
    return <String, dynamic>{
      'name': 'web_tests/artifacts',
      'drone_dimensions': <String>[
        'device_type=none',
        'os=Linux',
        'cores=32'
      ],
      'gclient_variables': <String, dynamic>{
        'download_android_deps': false,
        'download_jdk': false,
        'download_emsdk': true,
      },
      'gn': <String>[
        '--web',
        '--runtime-mode=release',
        '--no-goma',
      ],
      'ninja': <String, dynamic>{
        'config': 'wasm_release',
        'targets': <String>[
          'flutter/web_sdk:flutter_web_sdk_archive'
        ]
      },
      'archives': <dynamic>[
        <String, dynamic>{
          'name': 'wasm_release',
          'base_path': 'out/wasm_release/zip_archives/',
          'type': 'gcs',
          'include_paths': <String>[
            'out/wasm_release/zip_archives/flutter-web-sdk.zip'
          ],
          'realm': 'production',
        }
      ],
      'generators': <String, dynamic>{
        'tasks': <dynamic>[
          <String, dynamic>{
            'name': 'check licenses',
            'parameters': <String>[
              'check-licenses'
            ],
            'scripts': <String>[ 'flutter/lib/web_ui/dev/felt' ],

          },
          <String, dynamic>{
            'name': 'web engine analysis',
            'parameters': <String>[
              'analyze'
            ],
            'scripts': <String>[ 'flutter/lib/web_ui/dev/felt' ],
          },
          <String, dynamic>{
            'name': 'copy artifacts for web tests',
            'parameters': <String>[
              'test',
              '--copy-artifacts',
            ],
            'scripts': <String>[ 'flutter/lib/web_ui/dev/felt' ],
          },
        ]
      },
    };
  }

  Map<String, dynamic> _getBundleBuildStep(TestBundle bundle) {
    return <String, dynamic>{
      'name': 'web_tests/test_bundles/${bundle.name}',
      'drone_dimensions': <String>[
        'device_type=none',
        'os=Linux',
      ],
      'generators': <String, dynamic>{
        'tasks': <dynamic>[
          <String, dynamic>{
            'name': 'compile bundle ${bundle.name}',
            'parameters': <String>[
              'test',
              '--compile',
              '--bundle=${bundle.name}',
            ],
            'scripts': <String>[ 'flutter/lib/web_ui/dev/felt' ],
          }
        ]
      },
    };
  }

  Iterable<dynamic> _getAllTestSteps(List<TestSuite> suites, PackageLock packageLock) {
    return <dynamic>[
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.chrome),
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.firefox),
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Mac', BrowserName.safari, specificOS: 'Mac-13', cpu: 'arm64'),
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Windows', BrowserName.chrome)
    ];
  }

  Map<String, dynamic> _getTestStepForPlatformAndBrowser(
    List<TestSuite> suites,
    PackageLock packageLock,
    String platform,
    BrowserName browser, {
    String? specificOS,
    String? cpu,
  }) {
    final filteredSuites = suites.where((suite) => suite.runConfig.browser == browser);
    final bundles = filteredSuites.map((suite) => suite.testBundle).toSet();
    return <String, dynamic>{
      'name': '$platform run ${browser.name} suites',
      'recipe': 'engine_v2/tester_engine',
      'drone_dimensions': <String>[
        'device_type=none',
        'os=${specificOS ?? platform}',
        if (cpu != null) 'cpu=$cpu',
      ],
      'gclient_variables': <String, dynamic>{
        'download_android_deps': false,
        'download_jdk': false,
      },
      'dependencies': <String>[
        'web_tests/artifacts',
        ...bundles.map((bundle) => 'web_tests/test_bundles/${bundle.name}'),
      ],
      'test_dependencies': <dynamic>[
        <String, dynamic>{
          'dependency': 'goldctl',
          'version': 'git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd',
        },
        if (browser == BrowserName.chrome)
          <String, dynamic>{
            'dependency': 'chrome_and_driver',
            'version': packageLock.chromeLock.version,
          },
        if (browser == BrowserName.firefox)
          <String, dynamic>{
            'dependency': 'firefox',
            'version': 'version:${packageLock.firefoxLock.version}',
          }
      ],
      'tasks': filteredSuites.map((suite) => <String, dynamic> {
        'name': 'run suite ${suite.name}',
        'parameters': <String>[
          'test',
          '--run',
          '--suite=${suite.name}',
        ],
        'script': 'flutter/lib/web_ui/dev/felt',
      }).toList(),
    };
  }
}
